using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using Qreed.Diagnostics;
using System.IO;
using EnvDTE80;
using Qreed.CodePlex;
using System.Windows.Forms;

namespace NUnitit
{
    public enum NUnitProgramType
    {
        Console,
        GUI
    }

    public class NUnitit
    {
        private Connect _connect;
        private VersionChecker _versionChecker;

        /// <summary>
        /// Initializes a new instance of the <see cref="NUnitit"/> class.
        /// </summary>
        /// <param name="connect">The connect.</param>
        public NUnitit(Connect connect)
        {
            _connect = connect;

            CheckForNewVersion();
        }

        private void CheckForNewVersion()
        {
            try
            {
                TimeSpan ts = DateTime.Now.Subtract(Settings.Default.LastVersionCheck);

                if (ts.Days < 7)
                    return;

                _versionChecker = new VersionChecker();

                _versionChecker.ProjectHomePage = "http://nunitit.codeplex.com/";
                _versionChecker.VersionInfoUrl = "http://nunitit.codeplex.com/";
                _versionChecker.MatchExpression = @"<b>Current\ Version</b>:\ (?<Version>.+?)</div>";
                _versionChecker.Assembly = System.Reflection.Assembly.GetExecutingAssembly();
                _versionChecker.UseAssemblyFileVersion = true;
                _versionChecker.CheckForNewVersionComplete += new EventHandler<VersionCheckerEventArgs>(CheckForNewVersionComplete);
                _versionChecker.CheckForNewVersionASync();
            }
            catch 
            {
                // Eat any errors
            }
        }

        void CheckForNewVersionComplete(object sender, VersionCheckerEventArgs e)
        {
            try
            {
                if (e.NewVersionAvailable)
                {
                    string msg = "A new version of NUnitIt is available.\r\n\r\n" +
                                    "Local version: " + _versionChecker.LocalVersion.ToString() + "\r\n" +
                                    "Online version: " + _versionChecker.OnlineVersion.ToString() + "\r\n\r\n" +
                                    "Do you wish to visit the project homepage?";

                    WinWrapper w = new WinWrapper(_connect);
                    
                    if (MessageBox.Show(w, msg, "New version", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        System.Diagnostics.Process.Start(_versionChecker.ProjectHomePage);
                    }
                }

                Settings.Default.LastVersionCheck = DateTime.Now;
                Settings.Default.Save();
            }
            catch
            {
                // Eat any errors
            }
        }

        /// <summary>
        /// Launches the NUnit.
        /// </summary>
        /// <param name="program">The program.</param>
        public void LaunchNUnit(NUnitProgramType programType)
        {
            string projectOutputFile = null;

            try
            {
                Project project = Common.GetStartupProject(_connect.ApplicationObject.Solution);
                projectOutputFile = Common.GetProjectOutputFile(project);

                _connect.ApplicationObject.Solution.SolutionBuild.Build(true);
            }
            catch (Exception ex)
            {
                _connect.DisplayErrorMessage("Startup project", "Failed to locate the output of the startup project.\n" + ex.ToString());
                return;
            }

            string processToLaunch = LocateLaunchProgram(programType);

            if (string.IsNullOrEmpty(processToLaunch))
                return; // Error is handled inside LocateLaunchProgram

            try
            {
                StartAndDebugProcess(processToLaunch, "\"" + projectOutputFile + "\"");
            }
            catch (Exception ex)
            {
                _connect.DisplayErrorMessage("Failed starting process.", ex.ToString());
            }
        }

        private void StartAndDebugProcess(string execPath, string arguments)
        {
            Win32Process.ProcessInformation pi = Win32Process.CreateProcess(execPath, arguments, null, Win32Process.CreationFlags.CREATE_SUSPENDED);

            if (pi.ProcessId == 0)
            {
                throw (new Exception("Failed starting the process \"" + execPath + "\"."));
            }

            try
            {
                Debugger2 debugger = (Debugger2)_connect.ApplicationObject.Debugger;
                Transport transport = debugger.Transports.Item("Default");

                Processes procList = debugger.GetProcesses(transport, ".");

                Process2 p2 = null;

                for (int i = 1; i <= procList.Count; i++)
                {
                    if (procList.Item(i).ProcessID == pi.ProcessId)
                    {
                        p2 = (Process2)procList.Item(i);
                        break;
                    }
                }

                if (p2 == null)
                    throw (new Exception("Unable to locate process with id " + pi.ProcessId));

                Engine[] engines = new Engine[1];
                engines[0] = transport.Engines.Item("Managed");

                p2.Attach2(engines);
            }
            catch (Exception ex)
            {
                throw (new Exception("Error occured while attaching debugger to the process.", ex));
            }
            finally
            {
                Win32Process.ResumeThread(pi.ThreadHandle);
            }
        }

        private string LocateLaunchProgram(NUnitProgramType programType)
        {
            string processToLaunch = null;
            string executableName = null;

            switch (programType)
            {
                case NUnitProgramType.Console:
                    processToLaunch = Settings.Default.NUnitConsolePath;
                    executableName = Resources.NUnitConsoleExec;
                    break;

                case NUnitProgramType.GUI:
                    processToLaunch = Settings.Default.NUnitGUIPath;
                    executableName = Resources.NUnitGUIExec;
                    break;
            }

            try
            {
                if (string.IsNullOrEmpty(processToLaunch))
                {
                    processToLaunch = Common.GetNUnitExecPath(executableName);

                    if (string.IsNullOrEmpty(processToLaunch))
                    {
                        _connect.DisplayErrorMessage("Failed to locate NUnit", "Make sure NUnit is installed and/or the location is correct in the settings of NUnitit.");
                        _connect.DisplaySettings();
                        return null;
                    }
                }
            }
            catch (Exception ex)
            {
                _connect.DisplayErrorMessage("Error locating NUnit", ex.ToString());
                return null;
            }

            if (!string.IsNullOrEmpty(processToLaunch) &&
               !File.Exists(processToLaunch))
            {
                _connect.DisplayErrorMessage("File not found", "Unable to open the executable \"" + processToLaunch + "\"");
                return null;
            }

            return processToLaunch;
        }
    }
}
